import json
import os
import time
import uuid
import requests
from datetime import datetime, timedelta
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
from Crypto.Signature import pkcs1_15
from Crypto.Cipher import AES
from base64 import b64encode, b64decode
from django.conf import settings
import logging

logger = logging.getLogger(__name__)


class WeChatNativePayClient:
    """微信Native扫码支付客户端（生成二维码供用户扫码支付）"""

    def __init__(self):
        # 检查私钥文件是否存在
        key_path = settings.WECHAT_PAY["key_path"]
        if not os.path.exists(key_path):
            error_msg = (
                f"微信支付私钥文件不存在: {key_path}\n"
                f"请确保私钥文件 apiclient_key.pem 存在，或修改 settings.py 中的 "
                f"WECHAT_PAY['key_path'] 指向正确的文件路径"
            )
            logger.error(error_msg)
            raise FileNotFoundError(error_msg)
        
        # 读取商户私钥（与 payment/wxpay.py 保持一致）
        try:
            with open(key_path, "r") as f:
                private_key_content = f.read()
                self.private_key = RSA.import_key(private_key_content)
                logger.info(f"成功加载微信支付私钥: {key_path}")
        except Exception as e:
            error_msg = f"读取微信支付私钥文件失败: {key_path}, 错误: {str(e)}"
            logger.error(error_msg)
            raise IOError(error_msg) from e
        
        # 保存配置
        self.config = settings.WECHAT_PAY
        self.domain = self.config["domain"]
        self.native_order_url = self.config["native_order_url"]
        
        # 验证配置
        logger.info(f"微信支付配置 - 商户号: {self.config['mchid']}, 证书序列号: {self.config['cert_serial_no']}, 私钥路径: {key_path}")
    
    def _sign(self, method, url_path, body):
        """生成请求签名（与 payment/wxpay.py 保持一致）"""
        timestamp = str(int(time.time()))
        nonce = str(uuid.uuid4()).replace('-', '')
        
        # 确保 body 是字符串（GET 请求为空字符串）
        if body is None:
            body = ''
        body = str(body)
        
        # 构造签名信息（微信支付V3签名规范）
        # 格式：method\nurl_path\ntimestamp\nnonce\nbody\n
        sign_str = f"{method}\n{url_path}\n{timestamp}\n{nonce}\n{body}\n"
        
        # 记录签名信息（用于调试）
        logger.info(f"签名生成 - 方法: {method}, URL路径: {url_path}, Body长度: {len(body)}")
        
        # 计算签名
        h = SHA256.new(sign_str.encode('utf-8'))
        signature = pkcs1_15.new(self.private_key).sign(h)
        
        # 设置认证信息（与 payment/wxpay.py 格式完全一致）
        token = f'WECHATPAY2-SHA256-RSA2048 mchid="{self.config["mchid"]}",nonce_str="{nonce}",timestamp="{timestamp}",serial_no="{self.config["cert_serial_no"]}",signature="{b64encode(signature).decode()}"'
        return token

    def create_native_order(self, out_trade_no, total_amount, description):
        """
        创建Native支付订单（生成二维码链接）
        :param out_trade_no: 商户订单号（唯一）
        :param total_amount: 订单金额（分）
        :param description: 订单描述（如"商品购买"）
        :return: 二维码链接或错误信息
        """
        try:
            # 构建订单参数
            url = self.native_order_url
            # 提取URL路径（与 payment/wxpay.py 保持一致）
            url_path = url.split(self.domain)[-1]
            
            # 确保URL路径以 / 开头
            if not url_path.startswith('/'):
                url_path = '/' + url_path
            
            logger.info(f"创建订单 - URL: {url}, 路径: {url_path}")
            
            # 构建订单参数
            data = {
                "appid": self.config["appid"],
                "mchid": self.config["mchid"],
                "description": description,
                "out_trade_no": out_trade_no,
                "notify_url": self.config["notify_url"],
                "amount": {
                    "total": total_amount,  # 单位：分
                    "currency": "CNY"
                }
            }
            # 可选：设置支付有效期（5分钟）
            # time_expire 格式：YYYY-MM-DDTHH:mm:ss+TIMEZONE
            # 微信支付要求使用 UTC 时间，格式：YYYY-MM-DDTHH:mm:ssZ
            expire_time = datetime.utcnow() + timedelta(minutes=5)
            data["time_expire"] = expire_time.strftime("%Y-%m-%dT%H:%M:%SZ")
            
            # 将请求参数转换为JSON字符串
            body = json.dumps(data, ensure_ascii=False)
            logger.info(f"微信支付下单请求：{body}")
            
            # 生成认证签名
            token = self._sign('POST', url_path, body)
            
            # 发送请求
            headers = {
                'Content-Type': 'application/json',
                'Accept': 'application/json',
                'Authorization': token
            }
            
            response = requests.post(url, data=body, headers=headers, timeout=10)
            # 记录响应状态码和头部信息（用于调试）
            if settings.DEBUG:
                logger.debug(f"响应状态码: {response.status_code}")
                logger.debug(f"响应头: {dict(response.headers)}")
            result = response.json()
            logger.info(f"微信支付下单响应：{result}")
            
            if response.status_code == 200 and 'code_url' in result:
                # 返回二维码链接（code_url）
                return {
                    "success": True,
                    "code_url": result["code_url"],  # 用于生成二维码的链接
                    "out_trade_no": out_trade_no
                }
            else:
                # 微信支付接口返回错误
                err_msg = result.get('message', result.get('detail', '未知错误'))
                err_code = result.get('code', 'UNKNOWN')
                error_msg = f"下单失败: {err_msg}（错误码: {err_code}）"
                logger.error(f"微信支付下单失败: {error_msg}, 响应: {result}")
                return {"success": False, "error": error_msg}
        except Exception as e:
            logger.error(f"创建订单异常: {str(e)}", exc_info=True)
            return {"success": False, "error": f"创建订单异常: {str(e)}"}

    def _decrypt_notification(self, resource: dict) -> dict:
        """
        使用 APIv3 密钥对回调资源体进行 AES-256-GCM 解密
        """
        api_v3_key = self.config["api_key"]
        ciphertext_b64 = resource.get('ciphertext')
        nonce = resource.get('nonce')
        associated_data = resource.get('associated_data', 'transaction')

        if not api_v3_key or not ciphertext_b64 or not nonce:
            raise ValueError('回调解密参数缺失')

        key_bytes = api_v3_key.encode('utf-8')
        cipher_bytes = b64decode(ciphertext_b64)
        nonce_bytes = nonce.encode('utf-8')
        aad_bytes = associated_data.encode('utf-8') if associated_data else b''

        if len(cipher_bytes) < 16:
            raise ValueError('密文长度非法')
        data_bytes, tag_bytes = cipher_bytes[:-16], cipher_bytes[-16:]

        cipher = AES.new(key_bytes, AES.MODE_GCM, nonce=nonce_bytes)
        if aad_bytes:
            cipher.update(aad_bytes)
        plaintext = cipher.decrypt_and_verify(data_bytes, tag_bytes)
        return json.loads(plaintext.decode('utf-8'))

    def verify_pay_notify(self, request):
        """
        验证支付结果回调通知的签名和解密
        :param request: 微信回调的HTTP请求对象
        :return: 验证结果和订单数据
        """
        try:
            # 提取回调头部的签名信息
            signature = request.META.get("HTTP_WECHAT_PAY_SIGNATURE") or request.META.get("HTTP_WECHATPAY_SIGNATURE")
            timestamp = request.META.get("HTTP_WECHAT_PAY_TIMESTAMP") or request.META.get("HTTP_WECHATPAY_TIMESTAMP")
            nonce = request.META.get("HTTP_WECHAT_PAY_NONCE") or request.META.get("HTTP_WECHATPAY_NONCE")
            serial = request.META.get("HTTP_WECHAT_PAY_SERIAL") or request.META.get("HTTP_WECHATPAY_SERIAL")
            body = request.body.decode("utf-8")

            # 注意：完整的签名验证需要平台公钥，这里简化处理
            # 生产环境应该使用微信平台公钥验证签名
            if not all([signature, timestamp, nonce, serial]):
                logger.warning("回调签名信息不完整")
                return {"success": False, "error": "签名信息不完整"}

            # 解析回调数据
            notify_data = json.loads(body)
            logger.info(f"支付回调原始数据: {notify_data}")
            
            # 如果回调数据包含 resource 字段，说明是加密的，需要解密
            if 'resource' in notify_data:
                try:
                    # 解密回调数据
                    decrypted_data = self._decrypt_notification(notify_data['resource'])
                    logger.info(f"支付回调解密后数据: {decrypted_data}")
                    
                    # 从解密后的数据中提取订单信息
                    transaction_id = decrypted_data.get("transaction_id")
                    out_trade_no = decrypted_data.get("out_trade_no")
                    trade_state = decrypted_data.get("trade_state")
                    
                    return {
                        "success": True,
                        "data": transaction_id,  # 微信支付订单号
                        "out_trade_no": out_trade_no,  # 商户订单号
                        "trade_state": trade_state  # 支付状态（SUCCESS表示成功）
                    }
                except Exception as e:
                    logger.error(f"回调数据解密失败: {str(e)}", exc_info=True)
                    return {"success": False, "error": f"回调数据解密失败: {str(e)}"}
            else:
                # 如果没有 resource 字段，可能是未加密的回调（测试环境）
                logger.warning("回调数据未加密，使用原始数据")
                return {
                    "success": True,
                    "data": notify_data.get("transaction_id"),  # 微信支付订单号
                    "out_trade_no": notify_data.get("out_trade_no"),  # 商户订单号
                    "trade_state": notify_data.get("trade_state")  # 支付状态（SUCCESS表示成功）
                }
        except Exception as e:
            logger.error(f"回调验证异常: {str(e)}", exc_info=True)
            return {"success": False, "error": str(e)}

    def query_order(self, out_trade_no):
        """
        查询订单支付状态
        :param out_trade_no: 商户订单号
        :return: 订单状态信息
        """
        try:
            # 构建查询URL（与 payment/wxpay.py 完全一致）
            url = f"{self.domain}/v3/pay/transactions/out-trade-no/{out_trade_no}?mchid={self.config['mchid']}"
            # 提取URL路径（包含查询参数）- 与 payment/wxpay.py 保持一致
            # 使用 split 方法，取最后一部分（包含路径和查询参数）
            if self.domain in url:
                url_path = url.split(self.domain, 1)[1]  # 使用 maxsplit=1 只分割一次
            else:
                # 如果 domain 不在 URL 中，可能是相对路径
                from urllib.parse import urlparse
                parsed = urlparse(url)
                url_path = parsed.path
                if parsed.query:
                    url_path += '?' + parsed.query
            
            # 确保URL路径以 / 开头
            if not url_path.startswith('/'):
                url_path = '/' + url_path
            
            logger.info(f"查询订单 - 完整URL: {url}")
            logger.info(f"查询订单 - Domain: {self.domain}")
            logger.info(f"查询订单 - URL路径: {url_path}")
            
            # 生成认证签名（GET 请求，body 为空字符串）
            token = self._sign('GET', url_path, '')
            
            # 发送请求
            headers = {
                'Accept': 'application/json',
                'Authorization': token
            }
            
            response = requests.get(url, headers=headers, timeout=10)
            result = response.json()
            logger.info(f"订单查询响应 - 状态码: {response.status_code}")
            logger.info(f"订单查询响应 - 结果: {result}")
            
            if response.status_code == 200:
                return {
                    "success": True,
                    "trade_state": result.get("trade_state"),  # 支付状态
                    "trade_state_desc": result.get("trade_state_desc", "")  # 状态描述
                }
            else:
                err_msg = result.get('message', result.get('detail', '未知错误'))
                err_code = result.get('code', 'UNKNOWN')
                error_msg = f"查询失败: {err_msg}（错误码: {err_code}）"
                logger.error(f"微信支付查询订单失败: {error_msg}, 响应: {result}")
                return {"success": False, "error": error_msg}
        except Exception as e:
            logger.error(f"查询订单异常: {str(e)}", exc_info=True)
            return {"success": False, "error": f"查询订单异常: {str(e)}"}